using System;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using Mbs.Globalization;
using Mbs.Log;
using Mbs.Messaging;
using Mbs.Modularity.AddIns;
using Mbs.Reflection;
using Mbs.ComponentModel.Container;

namespace Mbs.Modularity
{
    [Component(Service=typeof(IModuleManager))]
    public class ModuleManager : MarshalByRefObject, IModuleManager,IInitializable
    {
        private readonly IModuleListner Listner;
        private readonly IServiceLocator Locator;
        private readonly IModuleRepository ModuleRepository;
        private readonly IModuleMetaRepository ModuleMetaRepository;
        private readonly IAddInTree AddInTree;
        private readonly IMessageBus MessageBus;
        private readonly IServiceRegistry ServiceRegistry;
        

        private readonly bool DefaultDomain;

        private static readonly ILog log = LoggerManager.GetLogger(typeof(ModuleManager));

        public ModuleManager(IAddInTree addInTree
            , IModuleRepository moduleRepository
            , IModuleMetaRepository moduleMetaRepository
            , IMessageBus messageBus
            , IServiceLocator locator)
        {
            AddInTree = addInTree;
            ModuleRepository = moduleRepository;
            ModuleMetaRepository = moduleMetaRepository;
            MessageBus = messageBus;
            Locator = locator;
            ServiceRegistry = locator.Get<IServiceRegistry>();
            DefaultDomain = true;

            Listner = ModuleListnerManager.Listner;
        }

        
        public void Init()
        {
            var meta = ModuleMeta.LoadSystemModule();
            if (meta != null && meta.AddIn != null)
            {
                AddInTree.Install(meta.AddIn);
                if (meta.AddIn.Runtime.Imports.Count() > 0)
                    foreach (var item in meta.AddIn.Runtime.Imports)
                        Assembly.LoadFrom(item);
                //if (meta.AddIn.Runtime.Services != null)
                //    ServiceRegistry.Load(meta.AddIn.Runtime.Services, ServiceLocator.ClassLoader);

                var asm = Assembly.GetEntryAssembly();
                ResourceRepository.StringRegistry.Register(meta.AddIn.StringResources, asm);
                ResourceRepository.ImageRegistry.Register(meta.AddIn.ImageResources, asm);
            }
        }

        private IClassLoader CreateClassLoader()
        {
            if (DefaultDomain)
                return ServiceLocator.Current.Get<IClassLoader>();
            return new RemoteClassLoader();
        }

        public void Start()
        {
            Debug.Assert(ModuleRepository != null);

            log.Debug("begin start modules......");

            var items = ModuleRepository.GetModules();
            if (items != null && items.Length > 0)
                foreach (var item in items)
                    item.Start();

            log.Debug("end start modules......");
        }

        public void Install()
        {
            Debug.Assert(ModuleMetaRepository != null);

            log.Debug("begin install modules......");

            var moduleMetas = ModuleMetaRepository.GetModules();
            if (moduleMetas != null && moduleMetas.Length > 0)
                foreach (var meta in moduleMetas)
                    InstallModule(meta);

            log.Debug("end install modules......");
        }

        public void Stop()
        {
            Debug.Assert(ModuleRepository != null);

            var items = ModuleRepository.GetModules().Reverse().ToArray();
            Array.ForEach<IModule>(items, (item) => item.Stop());
        }

        public void Shutdown()
        {
            Debug.Assert(ModuleRepository != null);

            var items = ModuleRepository.GetModules().Reverse().ToArray();
            Array.ForEach<IModule>(items, item =>
            {
                int id = item.Id;

                item.Stop();

                ModuleRepository.Unregister(item);
                if (item.Meta.AddIn != null && item.Meta.AddIn.Enabled)
                    AddInTree.Uninstall(item.Meta.AddIn);
                item = null;
            });
        }

        public IModule InstallModule(IModuleMeta info)
        {
            IModule module = null;

            if (info.IsSystem)
                return ModuleRepository.GetModule(0);

            try
            {
                var classLoader = CreateClassLoader();

                module = ServiceLocator.Current.Get<IModuleProvider>().CreateModule(info);

                CheckInstallBundle(module);
               
                InstallModuleInternal(module);
            }
            catch (Exception ex)
            {
                Listner.OnExcption(new ModuleEventArgs(module, ex));
            }
            return module;
        }

        public IModule InstallModule(string moduleFile)
        {
            Debug.Assert(ModuleRepository != null);
            Debug.Assert(string.IsNullOrEmpty(moduleFile), "moduleFile == null");

            var meta = ModuleMetaRepository.Register(moduleFile);
            return InstallModule(meta);
        }


        private void CheckInstallBundle(IModule module)
        {
            IModule item = ModuleRepository.GetModule(module.Meta.Name);
            if (item != null)
                throw new ModuleException("Module is already installed.");
        }

        private IModule InstallModuleInternal(IModule module)
        {
            ModuleRepository.Register(module);

            if (module.State == ModuleState.Installed)
                Listner.OnInstalled(new ModuleEventArgs(module));

            return module;
        }

        public void UninstallModule(int id)
        {
            Debug.Assert(ModuleRepository != null);
            IModule module = null;
            try
            {
                module = ModuleRepository.GetModule(id);
                ModuleRepository.Unregister(module);
                if (module.Meta.AddIn != null)
                    AddInTree.Uninstall(module.Meta.AddIn);
            }
            catch (Exception ex)
            {
                var newEx = new ModuleException("Uninstall module threw a exception.", ex);
               Listner.OnExcption(new ModuleEventArgs(module,newEx));
            }
        }

        public IModule StartModule(int id)
        {
            Debug.Assert(ModuleRepository != null);

            IModule module = null;

            try
            {
                module = ModuleRepository.GetModule(id);

                if (module == null)
                    throw new ModuleException(String.Format("Module not found.Module First:{0}", id));
                if (module.State != ModuleState.Installed)
                    throw new ModuleException("Module is aready started.");

                module.Start();
                return module;
            }
            catch (Exception ex)
            {
                Listner.OnExcption(new ModuleEventArgs(module, ex));
                return null;
            }
        }

        public void StopModule(int id)
        {
            Debug.Assert(ModuleRepository != null);
            IModule module = null;

            try
            {
                module = ModuleRepository.GetModule(id);
                if (module == null)
                    throw new ModuleException(String.Format("Module not found.Module First:{0}", id));
                if (module.State != ModuleState.Actived)
                    throw new ModuleException("Module is not active.");

                module.Stop();

            }
            catch (Exception ex)
            {
                Listner.OnExcption(new ModuleEventArgs(module, ex));
            }
        }

        public void StartModule(IModule module)
        {
            if (module != null)
                module.Start();
        }

        public void Dispose()
        {
            Shutdown();
        }

    }
}
